﻿
using PSE.Framework.Communication.Adapters.File;
using PSE.Framework.Communication.Configuration.Adapters;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Text;
using PSE.Framework.Communication.Configuration.Exceptions;
using System.Xml.Xsl;
using PSE.Framework.Communication.Configuration.File;
using PSE.Framework.Communication.Configuration.File.XmlFile;
using System.Configuration;

namespace PSE.Framework.Communication.Adapters.File.XmlFile 
{
    public class XmlFileOutputAdapter : FileOutputAdapter<XmlFileOutputConfiguration>
    {
        private Dictionary<string, XmlSerializer> _serializers;
        private XmlWriter _fileWriter;
        private string _transformPath;
        private XslCompiledTransform _transform;
        private bool _isCanceling;

		public XmlFileOutputAdapter()
        {
		}

        protected override void VerifyFileExists(string fullPath)
        {
            // Verificando arquivo transformado
            base.VerifyFileExists(fullPath);

            if (!string.IsNullOrEmpty(this.Configuration.TransformFile))
            {
                // Montando path do arquivo original
                string transformPath = this.GetTransformPath(fullPath);

                // Excluindo arquivo original
                if (System.IO.File.Exists(transformPath))
                    System.IO.File.Delete(transformPath);
            }
        }

        protected override void CreateAndOpenFile(string fullPath)
        {
            _serializers = new Dictionary<string, XmlSerializer>();
            string destPath;

            // Verifica se deve transformar o arquivo
            if (!string.IsNullOrEmpty(this.Configuration.TransformFile))
            {
                // Validando se a pasta de transformação foi informada
                if (string.IsNullOrEmpty(this.Configuration.TransformPath))
                    throw new InvalidAdapterConfigurationException("TransformPath must be informed with TransformFile", this.Configuration.Name);

                // Carregando Xslt
                _transform = new XslCompiledTransform();
                _transform.Load(this.Configuration.TransformFile);

                // Montando path do arquivo original
                _transformPath = this.GetTransformPath(fullPath);

                // Gera o arquivo na pasta do arquivo original
                destPath = _transformPath;
            }
            else
            {
                // Gera o arquivo na pasta final
                destPath = fullPath;
            }

            XmlWriterSettings settings = new XmlWriterSettings();
            // Identa o xml
            settings.Indent = true;
            // Encoding UTF-8
            settings.Encoding = Encoding.GetEncoding(this.Configuration.Encoding);

            // Criando writer
            _fileWriter = XmlWriter.Create(destPath, settings);

            // Verifica se deve criar tag root
            if (!string.IsNullOrEmpty(this.Configuration.Root))
                _fileWriter.WriteStartElement(this.Configuration.Root);

            _fileWriter.WriteRaw("\n");
        }

        private string GetTransformPath(string fullPath)
        {
            return this.Configuration.TransformPath + @"\" + Path.GetFileNameWithoutExtension(fullPath) + "_Original" + Path.GetExtension(fullPath);
        }

        protected override void Send(object message)
        {
            // Escreve string xml no arquivo
            _fileWriter.WriteRaw((string)message);
        }

        protected override object Serialize(object message, string action)
        {
            object serializedMessage = message;

            // Verifica se mensagem não é uma string
            if (!(message is string))
            {
                // Verifica se já existe um serializer para esta action
                if (!_serializers.ContainsKey(action))
                {
                    // Se não existir, cria serializer
                    _serializers.Add(action, new XmlSerializer(message.GetType()));
                }

                StringBuilder sb = new StringBuilder();

                XmlWriterSettings settings = new XmlWriterSettings();
                // Identa o xml
                settings.Indent = true;
                // Encoding UTF-8
                settings.Encoding = Encoding.GetEncoding(this.Configuration.Encoding);
                // Não gera declaração de xml
                settings.OmitXmlDeclaration = true;

                // Criando writer
                using (XmlWriter writer = XmlWriter.Create(sb, settings))
                {
                    // Remove as namespaces da tag root
                    XmlSerializerNamespaces namespaces = new XmlSerializerNamespaces();
                    namespaces.Add(string.Empty, string.Empty);

                    foreach (KeyValueConfigurationElement ns in this.Configuration.Namespaces)
                    {
                        namespaces.Add(ns.Key, ns.Value);
                    }


                    // Serializando objeto
                    _serializers[action].Serialize(writer, message, namespaces);

                    sb.Append('\n');
                    serializedMessage = sb.ToString();
                }
            }

            return serializedMessage;
        }

        protected override void CloseFile()
        {
            try
            {
                // Verifica se o writer foi criado
                if (_fileWriter != null)
                {
                    // Verifica se não está cancelando e se não ocorreu erro
                    if (!_isCanceling && _fileWriter.WriteState != WriteState.Error)
                    {
                        try
                        {
                            // Fechando se necessário a tag root
                            if (!string.IsNullOrEmpty(this.Configuration.Root))
                                _fileWriter.WriteEndElement();
                        }
                        finally
                        {
                            // Fechando arquivo
                            _fileWriter.Close();
                        }

                        // Verifica se deve transformar o arquivo
                        if (!string.IsNullOrEmpty(this.Configuration.TransformFile))
                        {
                            // Transformando arquivo
                            _transform.Transform(_transformPath, this.FullPath);
                        }
                    }
                    else
                    {
                        // Se está cancelando ou ocorreu erro somente fecha o arquivo
                        _fileWriter.Close();
                    }
                }
            }
            finally
            {
                _fileWriter = null;
                _serializers = null;
                _transform = null;
                _transformPath = null;
                _isCanceling = false;
            }
        }

        public override void CancelStream()
        {
            //se o stream estiver aberto
            if (StreamStatus == StreamStatus.Opened)
            {
                // Indica que está cancelando para o método CloseFile
                _isCanceling = true;

                base.CancelStream();
            }
        }

        protected override void DeleteFile(string fullPath)
        {
            // Excluindo arquivo original
            if (!string.IsNullOrEmpty(this.Configuration.TransformFile))
            {
                // Montando path do arquivo original
                string transformPath = this.GetTransformPath(fullPath);

                // Excluindo arquivo original
                base.DeleteFile(transformPath);
            }

            // Excluindo arquivo transformado            
            base.DeleteFile(fullPath);
        }

    }//end XmlFileOutputAdapter

}
